home *** CD-ROM | disk | FTP | other *** search
/ Revista do CD-ROM 98 / CD-ROM 98.iso / infantil / tuxmath / tuxmath-2001.09.07-win32-installer.exe / src / game.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-09-07  |  28.7 KB  |  1,469 lines

  1. /*
  2.   game.c
  3.  
  4.   For TuxMath
  5.   The main game loop!
  6.  
  7.   by Bill Kendrick
  8.   bill@newbreedsoftware.com
  9.   http://www.newbreedsoftware.com/
  10.  
  11.  
  12.   Part of "Tux4Kids" Project
  13.   http://www.tux4kids.org/
  14.       
  15.   August 26, 2001 - September 7, 2001
  16. */
  17.  
  18.  
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <SDL.h>
  23. #ifndef NOSOUND
  24. #include <SDL_mixer.h>
  25. #endif
  26. #include <SDL_image.h>
  27. #include "game.h"
  28. #include "images.h"
  29. #include "setup.h"
  30. #include "sounds.h"
  31. #include "playsound.h"
  32.  
  33.  
  34. #define FPS (1000 / 15)   /* 15 fps max */
  35.  
  36. #define CITY_EXPL_START 3 * 5  /* Must be mult. of 5 (number of expl frames) */
  37. #define COMET_EXPL_START 2 * 2 /* Must be mult. of 2 (number of expl frames) */
  38. #define ANIM_FRAME_START 4 * 2 /* Must be mult. of 2 (number of tux frames) */
  39. #define GAMEOVER_COUNTER_START 75
  40. #define LEVEL_START_WAIT_START 20
  41. #define LASER_START 5
  42.  
  43.  
  44.  
  45. /* Local (to game.c) 'globals': */
  46.  
  47. int wave, speed, score, pre_wave_score, num_attackers;
  48. int digits[3];
  49. comet_type comets[MAX_COMETS];
  50. city_type cities[NUM_CITIES];
  51. laser_type laser;
  52. SDL_Surface * bkgd;
  53. int last_bkgd;
  54.  
  55.  
  56.  
  57. /* Local function prototypes: */
  58.  
  59. void reset_level(void);
  60. void add_comet(void);
  61. void draw_nums(char * str, int x, int y);
  62. void draw_numbers(char * str, int x);
  63. int pause_game(void);
  64. void draw_line(int x1, int y1, int x2, int y2, int r, int g, int b);
  65. void putpixel(SDL_Surface * surface, int x, int y, Uint32 pixel);
  66. void draw_console_image(int i);
  67. void add_score(int inc);
  68.  
  69.  
  70.  
  71. /* --- MAIN GAME FUNCTION!!! --- */
  72.  
  73. int game(void)
  74. {
  75.   int i, j, num, img, done, quit, frame, lowest, lowest_y, kx, ky,
  76.     tux_img, old_tux_img, tux_pressing, tux_anim, tux_anim_frame,
  77.     tux_same_counter, level_start_wait, num_cities_alive, doing_answer,
  78.     num_comets_alive, paused, demo_countdown, picked_comet, answer_digit,
  79.     gameover;
  80.   SDL_Event event;
  81.   Uint32 last_time, now_time;
  82.   SDLKey key;
  83.   SDL_Rect src, dest;
  84.   char str[64];
  85.  
  86.  
  87.   /* Clear window: */
  88.   
  89.   SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
  90.   SDL_Flip(screen);
  91.   
  92.   
  93.   /* --- MAIN GAME LOOP: --- */
  94.  
  95.   done = 0;
  96.   quit = 0;
  97.   
  98.   
  99.   /* Prepare to start the game: */
  100.   
  101.   wave = 1;
  102.   score = 0;
  103.   gameover = 0;
  104.   demo_countdown = 1000;
  105.   level_start_wait = LEVEL_START_WAIT_START;
  106.  
  107.   
  108.   /* (Create and position cities) */
  109.   
  110.   for (i = 0; i < NUM_CITIES; i++)
  111.     {
  112.       cities[i].alive = 1;
  113.       cities[i].expl = 0;
  114.       cities[i].shields = 1;
  115.      
  116.  
  117.       /* Left vs. Right - makes room for Tux and the console */
  118.  
  119.       if (i < NUM_CITIES / 2)
  120.     {
  121.       cities[i].x = (((screen->w / (NUM_CITIES + 1)) * i) +
  122.              ((images[IMG_CITY_BLUE] -> w) / 2));
  123.     }
  124.       else
  125.     {
  126.       cities[i].x = (screen->w -
  127.              ((((screen->w / (NUM_CITIES + 1)) *
  128.                 (i - (NUM_CITIES / 2)) +
  129.                 ((images[IMG_CITY_BLUE] -> w) / 2)))));
  130.     }
  131.     }
  132.  
  133.   num_cities_alive = NUM_CITIES;
  134.   num_comets_alive = 0;
  135.  
  136.  
  137.   /* (Clear laser) */
  138.  
  139.   laser.alive = 0;
  140.  
  141.   
  142.   /* Reset remaining stuff: */
  143.  
  144.   bkgd = NULL;
  145.   last_bkgd = -1;
  146.   reset_level();
  147.   
  148.   
  149.   /* --- MAIN GAME LOOP!!! --- */
  150.   
  151.   frame = 0;
  152.   paused = 0;
  153.   picked_comet = -1;
  154.   answer_digit = 0;
  155.   doing_answer = 0;
  156.   tux_img = IMG_TUX_RELAX1;
  157.   tux_anim = -1;
  158.   tux_anim_frame = 0;
  159.   tux_same_counter = 0;
  160.  
  161.   
  162.   do
  163.     {
  164.       frame++;
  165.       last_time = SDL_GetTicks();
  166.  
  167.  
  168.       /* Handle any incoming events: */
  169.      
  170.       old_tux_img = tux_img;
  171.       tux_pressing = 0;
  172.  
  173.       while (SDL_PollEvent(&event) > 0)
  174.     {
  175.       if (event.type == SDL_QUIT)
  176.         {
  177.           /* Window close event - quit! */
  178.           
  179.           quit = 1;
  180.           done = 1;
  181.         }
  182.       else if (event.type == SDL_KEYDOWN)
  183.         {
  184.           key = event.key.keysym.sym;
  185.           
  186.           
  187.           if (key == SDLK_ESCAPE)
  188.         {
  189.           /* Escape key - quit! */
  190.           
  191.           done = 1;
  192.         }
  193.           else if (key == SDLK_TAB ||
  194.                key == SDLK_p)
  195.         {
  196.           /* [TAB] or [P]: Pause! */
  197.           
  198.           paused = 1;
  199.         }
  200.           
  201.           
  202.           if (level_start_wait > 0 || demo_mode)
  203.         {
  204.           /* Eat other keys until level start wait has passed,
  205.              or if game is in demo mode: */
  206.           
  207.           key = SDLK_UNKNOWN;
  208.         }
  209.           
  210.           
  211.           if (key >= SDLK_0 && key <= SDLK_9)
  212.         {
  213.           /* [0]-[9]: Add a new digit: */
  214.           
  215.           digits[0] = digits[1];
  216.           digits[1] = digits[2];
  217.           digits[2] = key - SDLK_0;
  218.           
  219.           tux_pressing = 1;
  220.         }
  221.           else if (key >= SDLK_KP0 && key <= SDLK_KP9)
  222.         {
  223.           /* Keypad [0]-[9]: Add a new digit: */
  224.           
  225.           digits[0] = digits[1];
  226.           digits[1] = digits[2];
  227.           digits[2] = key - SDLK_KP0;
  228.           
  229.           tux_pressing = 1;
  230.         }
  231.           else if (key == SDLK_BACKSPACE ||
  232.                key == SDLK_CLEAR ||
  233.                key == SDLK_DELETE)
  234.         {
  235.           /* [BKSP]: Clear digits! */
  236.           
  237.           digits[0] = 0;
  238.           digits[1] = 0;
  239.           digits[2] = 0;
  240.           
  241.           tux_pressing = 1;
  242.         }
  243.           else if (key == SDLK_RETURN ||
  244.                key == SDLK_KP_ENTER ||
  245.                key == SDLK_SPACE)
  246.         {
  247.           /* [ENTER]: Accept digits! */
  248.          
  249.           doing_answer = 1;
  250.         }
  251.         }
  252.       else if (event.type == SDL_MOUSEBUTTONDOWN)
  253.         {
  254.               if (level_start_wait == 0 && !demo_mode)
  255.           {
  256.             if (event.button.x >=
  257.                 (screen->w / 2) - (images[IMG_KEYPAD]->w / 2) &&
  258.                     event.button.x <=
  259.             (screen->w / 2) + (images[IMG_KEYPAD]->w / 2) &&
  260.             event.button.y >= 
  261.             (screen->h / 2) - (images[IMG_KEYPAD]->h / 2) &&
  262.             event.button.y <=
  263.             (screen->h / 2) + (images[IMG_KEYPAD]->h / 2))
  264.             {
  265.           kx = (event.button.x -
  266.                 ((screen->w / 2) - (images[IMG_KEYPAD]->w / 2)));
  267.           ky = (event.button.y -
  268.                 ((screen->h / 2) - (images[IMG_KEYPAD]->h / 2)));
  269.  
  270.           tux_pressing = 1;
  271.  
  272.  
  273.           if (ky >= (images[IMG_KEYPAD]->h / 4) * 3)
  274.           {
  275.                 /* Bottom row is special (has Enter key) */
  276.               
  277.                 if (kx >= (images[IMG_KEYPAD]->w / 3))
  278.             {
  279.               /* "Enter" key */
  280.  
  281.               doing_answer = 1;
  282.  
  283.               tux_pressing = 0;
  284.             }
  285.                     else
  286.             {
  287.               /* "0" key */
  288.                 
  289.               digits[0] = digits[1];
  290.               digits[1] = digits[2];
  291.               digits[2] = 0;
  292.             }
  293.           }
  294.           else
  295.           {
  296.                 digits[0] = digits[1];
  297.             digits[1] = digits[2];
  298.             digits[2] = (((kx / (images[IMG_KEYPAD]->w / 3)) + 1) +
  299.                  6 - ((ky / (images[IMG_KEYPAD]->h / 4) * 3)));
  300.           }
  301.             }
  302.           }
  303.         }
  304.     }
  305.  
  306.  
  307.  
  308.       if (demo_mode)
  309.       {
  310.         /* Demo mdoe! */
  311.  
  312.         if (picked_comet == -1 && (rand() % 10) < 3)
  313.         {
  314.       /* Demo mode!  Randomly pick a comet to destroy: */
  315.     
  316.       picked_comet = (rand() % MAX_COMETS);
  317.     
  318.       if (!comets[picked_comet].alive || comets[picked_comet].y < 80)
  319.             picked_comet = -1;
  320.       else
  321.       {
  322.         if (comets[picked_comet].answer >= 100)
  323.           answer_digit = 0;
  324.         else if (comets[picked_comet].answer >= 10)
  325.           answer_digit = 1;
  326.         else
  327.               answer_digit = 2;
  328.       }
  329.         }
  330.       
  331.  
  332.         /* Add a digit: */
  333.  
  334.     if (picked_comet != -1 && (frame % 5) == 0 && (rand() % 10) < 8)
  335.     {
  336.           tux_pressing = 1;
  337.       
  338.           if (answer_digit < 3)
  339.       {
  340.         digits[0] = digits[1];
  341.         digits[1] = digits[2];
  342.  
  343.         if (answer_digit == 0)
  344.         {
  345.           digits[2] = comets[picked_comet].answer / 100;
  346.         }
  347.         else if (answer_digit == 1)
  348.         {
  349.           digits[2] = (comets[picked_comet].answer % 100) / 10;
  350.         }
  351.         else if (answer_digit == 2)
  352.         {
  353.           digits[2] = (comets[picked_comet].answer % 10);
  354.         }
  355.         
  356.             answer_digit++;
  357.       }
  358.       else
  359.       {
  360.             /* "Press Return" */
  361.  
  362.         doing_answer = 1;
  363.         picked_comet = -1;
  364.       }
  365.     }
  366.  
  367.  
  368.         /* Count down counter: */
  369.     
  370.     demo_countdown--;
  371.     
  372.     if (demo_countdown <= 0 || num_cities_alive == 0)
  373.           done = 1;
  374.       }
  375.       
  376.       
  377.       /* Handle answer: */
  378.       
  379.       if (doing_answer)
  380.       {
  381.     doing_answer = 0;
  382.  
  383.     num = (digits[0] * 100 +
  384.            digits[1] * 10 +
  385.            digits[2]);
  386.     
  387.     
  388.     /*  Pick the lowest comet which has the right answer: */
  389.     
  390.     lowest_y = 0;
  391.     lowest = -1;
  392.     
  393.     for (i = 0; i < MAX_COMETS; i++)
  394.       {
  395.         if (comets[i].alive &&
  396.             comets[i].expl == 0 && 
  397.             comets[i].answer == num &&
  398.             comets[i].y > lowest_y)
  399.           {
  400.             lowest = i;
  401.             lowest_y = comets[i].y;
  402.           }
  403.       }
  404.     
  405.     
  406.     /* If there was an comet with this answer, destroy it! */
  407.     
  408.     if (lowest != -1)
  409.       {
  410.             /* Destroy comet: */
  411.           
  412.         comets[lowest].expl = COMET_EXPL_START;
  413.         
  414.  
  415.         /* Fire laser: */
  416.         
  417.         laser.alive = LASER_START;
  418.         
  419.         laser.x1 = screen->w / 2;
  420.         laser.y1 = screen->h;
  421.         
  422.         laser.x2 = comets[lowest].x;
  423.         laser.y2 = comets[lowest].y;
  424.         
  425.         playsound(SND_LASER);
  426.         
  427.         
  428.         /* 50% of the time.. */
  429.         
  430.         if ((rand() % 10) < 5)
  431.           {
  432.             /* ... pick an animation to play: */
  433.             
  434.             if ((rand() % 10) < 5)
  435.               tux_anim = IMG_TUX_YES1;
  436.             else
  437.               tux_anim = IMG_TUX_YAY1;
  438.             
  439.             tux_anim_frame = ANIM_FRAME_START;
  440.           }
  441.  
  442.  
  443.         /* Increment score: */
  444.  
  445.         /* [ add = 25, sub = 50, mul = 75, div = 100 ] */
  446.         /* [ the higher the better ] */
  447.  
  448.         add_score(((25 * (comets[lowest].oper + 1)) *
  449.                 (screen->h - comets[lowest].y + 1)) /
  450.               screen->h);
  451.       }
  452.     else
  453.       {
  454.         /* Didn't hit anything! */
  455.         
  456.         laser.alive = LASER_START;
  457.         
  458.         laser.x1 = screen->w / 2;
  459.         laser.y1 = screen->h;
  460.         
  461.         laser.x2 = laser.x1;
  462.         laser.y2 = 0;
  463.         
  464.         playsound(SND_LASER);
  465.         playsound(SND_BUZZ);
  466.         
  467.         if ((rand() % 10) < 5)
  468.           tux_img = IMG_TUX_DRAT;
  469.         else
  470.           tux_img = IMG_TUX_YIPE;
  471.       }
  472.     
  473.     
  474.     /* Clear digits: */
  475.     
  476.     digits[0] = 0;
  477.     digits[1] = 0;
  478.     digits[2] = 0;
  479.       }
  480.  
  481.       
  482.       /* Handle start-wait countdown: */
  483.       
  484.       if (level_start_wait > 0)
  485.     {
  486.       level_start_wait--;
  487.       
  488.       if (level_start_wait > LEVEL_START_WAIT_START / 4)
  489.         tux_img = IMG_TUX_RELAX1;
  490.       else if (level_start_wait > 0)
  491.         tux_img = IMG_TUX_RELAX2;
  492.       else
  493.         tux_img = IMG_TUX_SIT;
  494.       
  495.       if (level_start_wait == LEVEL_START_WAIT_START / 4)
  496.         {
  497.           playsound(SND_ALARM);
  498.         }
  499.     }
  500.  
  501.       
  502.       /* If Tux pressed a button, pick a new (different!) stance: */
  503.       
  504.       if (tux_pressing)
  505.       {
  506.         do
  507.     {
  508.       tux_img = IMG_TUX_CONSOLE1 + (rand() % 4);
  509.     }
  510.         while (tux_img == old_tux_img);
  511.  
  512.     playsound(SND_CLICK);
  513.       }
  514.       
  515.       
  516.       /* If Tux is being animated, show the animation: */
  517.  
  518.       if (tux_anim != -1)
  519.       {
  520.     tux_anim_frame--;
  521.  
  522.     if (tux_anim_frame < 0)
  523.           tux_anim = -1;
  524.     else
  525.       tux_img = tux_anim + 1 - (tux_anim_frame / (ANIM_FRAME_START / 2));
  526.       }
  527.  
  528.  
  529.       /* Reset Tux to sitting if he's been doing nothing for a while: */
  530.  
  531.       if (old_tux_img == tux_img)
  532.       {
  533.     tux_same_counter++;
  534.  
  535.     if (tux_same_counter >= 20)
  536.     {
  537.           tux_img = IMG_TUX_SIT;
  538.     }
  539.       }
  540.       else
  541.     tux_same_counter = 0;
  542.  
  543.  
  544.       /* Handle comets: */
  545.      
  546.       num_comets_alive = 0;
  547.       
  548.       for (i = 0; i < MAX_COMETS; i++)
  549.     {
  550.       if (comets[i].alive)
  551.         {
  552.           num_comets_alive++;
  553.  
  554.           comets[i].x = comets[i].x + 0;
  555.           comets[i].y = comets[i].y + speed;
  556.           
  557.           if (comets[i].y >= (screen->h - images[IMG_CITY_BLUE]->h) &&
  558.               comets[i].expl == 0)
  559.           {
  560.         /* Disable shields or destroy city: */
  561.               
  562.         if (cities[comets[i].city].shields)
  563.         {
  564.               cities[comets[i].city].shields = 0;
  565.           playsound(SND_SHIELDSDOWN);
  566.         }
  567.         else
  568.         {
  569.           cities[comets[i].city].expl = CITY_EXPL_START;
  570.           playsound(SND_EXPLOSION);
  571.         }
  572.  
  573.         tux_anim = IMG_TUX_FIST1;
  574.         tux_anim_frame = ANIM_FRAME_START;
  575.  
  576.  
  577.         /* Destroy comet: */
  578.  
  579.         comets[i].expl = COMET_EXPL_START;
  580.           }
  581.  
  582.  
  583.           /* Handle comet explosion animation: */
  584.  
  585.           if (comets[i].expl != 0)
  586.           {
  587.         comets[i].expl--;
  588.  
  589.         if (comets[i].expl == 0)
  590.               comets[i].alive = 0;
  591.           }
  592.         }
  593.     }
  594.  
  595.  
  596.       /* Handle laser: */
  597.  
  598.       if (laser.alive > 0)
  599.     laser.alive--;
  600.       
  601.      
  602.       /* Comet time! */
  603.  
  604.       if (level_start_wait == 0 && (frame % 20) == 0 &&
  605.       gameover == 0)
  606.       {
  607.     if (num_attackers > 0)
  608.     {
  609.           /* More comets to add during this wave! */
  610.         
  611.       if ((rand() % 2) == 0 || num_comets_alive == 0)
  612.       {
  613.             add_comet();
  614.         num_attackers--;
  615.       }
  616.     }
  617.     else
  618.     {
  619.           if (num_comets_alive == 0)
  620.       {
  621.             /* Time for the next wave! */
  622.  
  623.         /* FIXME: End of level stuff goes here */
  624.  
  625.         if (num_cities_alive > 0)
  626.         {
  627.               /* Go on to the next wave: */
  628.           
  629.               wave++;
  630.           reset_level();
  631.         }
  632.         else
  633.         {
  634.               /* No more cities!  Game over! */
  635.  
  636.           gameover = GAMEOVER_COUNTER_START;
  637.         }
  638.       }
  639.     }
  640.       }
  641.  
  642.  
  643.       /* Handle cities: */
  644.      
  645.       num_cities_alive = 0;
  646.  
  647.       for (i = 0; i < NUM_CITIES; i++)
  648.     {
  649.       if (cities[i].alive)
  650.         {
  651.           num_cities_alive++;
  652.  
  653.  
  654.           /* Handle animated explosion: */
  655.  
  656.           if (cities[i].expl)
  657.         {
  658.           cities[i].expl--;
  659.           
  660.           if (cities[i].expl == 0)
  661.             cities[i].alive = 0;
  662.         }
  663.         }
  664.     }
  665.  
  666.  
  667.       /* Handle game-over: */
  668.  
  669.       if (gameover > 0)
  670.       {
  671.     gameover--;
  672.  
  673.     if (gameover <= 0)
  674.           done = 1;
  675.       }
  676.       
  677.       
  678.       /* Clear screen: */
  679.      
  680.       if (bkgd == NULL)
  681.       {
  682.         dest.x = 0;
  683.         dest.y = 0;
  684.         dest.w = screen->w;
  685.         dest.h = ((screen->h) / 4) * 3;
  686.  
  687.         SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format,
  688.              64,
  689.              64 + ((wave * 32) % 192),
  690.              128 - ((wave * 16) % 128)));
  691.  
  692.  
  693.         dest.x = 0;
  694.         dest.y = ((screen->h) / 4) * 3;
  695.         dest.w = screen->w;
  696.         dest.h = (screen->h) / 4;
  697.  
  698.         SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 64, 96, 64));
  699.       }
  700.       else
  701.         SDL_BlitSurface(bkgd, NULL, screen, NULL);
  702.  
  703.  
  704.       /* Draw "Demo" */
  705.  
  706.       if (demo_mode)
  707.       {
  708.     dest.x = (screen->w - images[IMG_DEMO]->w) / 2;
  709.     dest.y = (screen->h - images[IMG_DEMO]->h) / 2;
  710.     dest.w = images[IMG_DEMO]->w;
  711.     dest.h = images[IMG_DEMO]->h;
  712.  
  713.     SDL_BlitSurface(images[IMG_DEMO], NULL, screen, &dest);
  714.       }
  715.  
  716.  
  717.       /* Draw wave: */
  718.  
  719.       dest.x = 0;
  720.       dest.y = 0;
  721.       dest.w = images[IMG_WAVE]->w;
  722.       dest.h = images[IMG_WAVE]->h;
  723.  
  724.       SDL_BlitSurface(images[IMG_WAVE], NULL, screen, &dest);
  725.  
  726.       sprintf(str, "%d", wave);
  727.       draw_numbers(str, images[IMG_WAVE]->w + (images[IMG_NUMBERS]->w / 10));
  728.  
  729.  
  730.       /* Draw score: */
  731.  
  732.       dest.x = (screen->w - ((images[IMG_NUMBERS]->w / 10) * 7) -
  733.             images[IMG_SCORE]->w);
  734.       dest.y = 0;
  735.       dest.w = images[IMG_SCORE]->w;
  736.       dest.h = images[IMG_SCORE]->h;
  737.  
  738.       SDL_BlitSurface(images[IMG_SCORE], NULL, screen, &dest);
  739.       
  740.       sprintf(str, "%.6d", score);
  741.       draw_numbers(str, screen->w - ((images[IMG_NUMBERS]->w / 10) * 6));
  742.       
  743.       
  744.       /* Draw comets: */
  745.       
  746.       for (i = 0; i < MAX_COMETS; i++)
  747.     {
  748.       if (comets[i].alive)
  749.         {
  750.           if (comets[i].expl == 0)
  751.           {
  752.             /* Decide which image to display: */
  753.           
  754.             img = IMG_COMET1 + ((frame + i) % 3);
  755.           }
  756.           else
  757.           {
  758.         img = (IMG_COMETEX2 -
  759.                (comets[i].expl / (COMET_EXPL_START / 2)));
  760.           }
  761.           
  762.  
  763.           /* Draw it! */
  764.  
  765.           dest.x = comets[i].x - (images[img]->w / 2);
  766.           dest.y = comets[i].y - images[img]->h;
  767.           dest.w = images[img]->w;
  768.           dest.h = images[img]->h;
  769.           
  770.           SDL_BlitSurface(images[img], NULL, screen, &dest);
  771.         }
  772.     }
  773.  
  774.  
  775.       /* Draw equations: */
  776.  
  777.       for (i = 0; i < MAX_COMETS; i++)
  778.       {
  779.     if (comets[i].alive && ((comets[i].y < screen->h / 2) ||
  780.             (frame % 8) < 6) && comets[i].expl == 0)
  781.     {
  782.       sprintf(str, "%d%c%d",
  783.               comets[i].eq1,
  784.               operchars[comets[i].oper],
  785.               comets[i].eq2);
  786.  
  787.           draw_nums(str, comets[i].x, comets[i].y);
  788.     }
  789.       }
  790.       
  791.       
  792.       /* Draw cities: */
  793.       
  794.       for (i = 0; i < NUM_CITIES; i++)
  795.     {
  796.       /* Decide which image to display: */
  797.      
  798.       if (cities[i].alive)
  799.         {
  800.           if (cities[i].expl == 0)
  801.         img = IMG_CITY_BLUE;
  802.           else
  803.         img = (IMG_CITY_BLUE_EXPL5 -
  804.                    (cities[i].expl / (CITY_EXPL_START / 5)));
  805.         }
  806.       else
  807.         img = IMG_CITY_BLUE_DEAD;
  808.       
  809.       
  810.       /* Change image to appropriate color: */
  811.       
  812.       img = img + ((wave % MAX_CITY_COLORS) *
  813.                (IMG_CITY_GREEN - IMG_CITY_BLUE));
  814.       /* img = img + ((i % MAX_CITY_COLORS) *
  815.                    (IMG_CITY_GREEN - IMG_CITY_BLUE)); */
  816.       
  817.       
  818.       /* Draw it! */
  819.       
  820.       dest.x = cities[i].x - (images[img]->w / 2);
  821.       dest.y = (screen->h) - (images[img]->h);
  822.       dest.w = (images[img]->w);
  823.       dest.h = (images[img]->h);
  824.       
  825.       SDL_BlitSurface(images[img], NULL,
  826.               screen, &dest);
  827.  
  828.  
  829.       /* Draw sheilds: */
  830.  
  831.       if (cities[i].shields)
  832.       {
  833.             for (j = (frame % 3); j < images[IMG_SHIELDS]->h; j = j + 3)
  834.         {
  835.           src.x = 0;
  836.           src.y = j;
  837.           src.w = images[IMG_SHIELDS]->w;
  838.           src.h = 1;
  839.  
  840.           dest.x = cities[i].x - (images[IMG_SHIELDS]->w / 2);
  841.           dest.y = (screen->h) - (images[IMG_SHIELDS]->h) + j;
  842.           dest.w = src.w;
  843.           dest.h = src.h;
  844.  
  845.           SDL_BlitSurface(images[IMG_SHIELDS], &src, screen, &dest);
  846.         }
  847.       }
  848.     }
  849.  
  850.  
  851.       /* Draw laser: */
  852.  
  853.       if (laser.alive)
  854.       {
  855.     draw_line(laser.x1, laser.y1, laser.x2, laser.y2,
  856.           255 / (LASER_START - laser.alive),
  857.           192 / (LASER_START - laser.alive),
  858.           64);
  859.       }
  860.  
  861.  
  862.       /* Draw numeric keypad: */
  863.  
  864.       if (use_keypad)
  865.       {
  866.         dest.x = (screen->w - images[IMG_KEYPAD]->w) / 2;
  867.         dest.y = (screen->h - images[IMG_KEYPAD]->h) / 2;
  868.         dest.w = images[IMG_KEYPAD]->w;
  869.         dest.h = images[IMG_KEYPAD]->h;
  870.  
  871.         SDL_BlitSurface(images[IMG_KEYPAD], NULL, screen, &dest);
  872.       }
  873.  
  874.  
  875.       /* Draw console & tux: */
  876.  
  877.       draw_console_image(IMG_CONSOLE);
  878.  
  879.       if (gameover > 0)
  880.       {
  881.     tux_img = IMG_TUX_FIST1 + ((frame / 2) % 2);
  882.       }
  883.  
  884.       draw_console_image(tux_img);
  885.  
  886.  
  887.       /* Draw LED digits at the top of the screen: */
  888.       
  889.       for (i = 0; i < 3; i++)
  890.     {
  891.       src.x = digits[i] * ((images[IMG_LEDNUMS]->w) / 10);
  892.       src.y = 0;
  893.       src.w = (images[IMG_LEDNUMS]->w) / 10;
  894.       src.h = images[IMG_LEDNUMS]->h;
  895.       
  896.       dest.x = (((screen->w - (((images[IMG_LEDNUMS]->w) / 10) * 3)) / 2) +
  897.             (i * (images[IMG_LEDNUMS]->w) / 10));
  898.       dest.y = 4;
  899.       dest.w = src.w;
  900.       dest.h = src.h;
  901.       
  902.       SDL_BlitSurface(images[IMG_LEDNUMS], &src, screen, &dest);
  903.     }
  904.  
  905.  
  906.       /* Draw "Game Over" */
  907.  
  908.       if (gameover > 0)
  909.       {
  910.     dest.x = (screen->w - images[IMG_GAMEOVER]->w) / 2;
  911.     dest.y = (screen->h - images[IMG_GAMEOVER]->h) / 2;
  912.     dest.w = images[IMG_GAMEOVER]->w;
  913.     dest.h = images[IMG_GAMEOVER]->h;
  914.     
  915.         SDL_BlitSurface(images[IMG_GAMEOVER], NULL, screen, &dest);
  916.       }
  917.       
  918.       
  919.       /* Swap buffers: */
  920.       
  921.       SDL_Flip(screen);
  922.  
  923.  
  924.       /* If we're in "PAUSE" mode, pause! */
  925.  
  926.       if (paused)
  927.         {
  928.       quit = pause_game();
  929.       paused = 0;
  930.         }
  931.  
  932.       
  933.       /* Keep playing music: */
  934.       
  935. #ifndef NOSOUND
  936.       if (use_sound)
  937.     {
  938.       if (!Mix_PlayingMusic())
  939.         Mix_PlayMusic(musics[MUS_GAME + (rand() % 3)], 0);
  940.     }
  941. #endif
  942.       
  943.       
  944.       /* Pause (keep frame-rate event) */
  945.       
  946.       now_time = SDL_GetTicks();
  947.       if (now_time < last_time + FPS)
  948.     SDL_Delay(last_time + FPS - now_time);
  949.     }
  950.   while (!done && !quit);
  951.  
  952.   
  953.   /* Free background: */
  954.  
  955.   if (bkgd != NULL)
  956.     SDL_FreeSurface(bkgd);
  957.  
  958.  
  959.  
  960.   /* Stop music: */
  961. #ifndef NOSOUND
  962.   if (use_sound)
  963.   {
  964.     if (Mix_PlayingMusic())
  965.     {
  966.       Mix_HaltMusic();
  967.     }
  968.   }
  969. #endif
  970.   
  971.   
  972.   /* Return the chosen command: */
  973.   
  974.   return quit;
  975. }
  976.  
  977.  
  978. /* Reset stuff for the next level! */
  979.  
  980. void reset_level(void)
  981. {
  982.   char fname[1024];
  983.   int i;
  984.   
  985.   
  986.   /* Clear all comets: */
  987.   
  988.   for (i = 0; i < MAX_COMETS; i++)
  989.     comets[i].alive = 0;
  990.   
  991.   
  992.   /* Clear LED digits: */
  993.   
  994.   digits[0] = 0;
  995.   digits[1] = 0;
  996.   digits[2] = 0;
  997.  
  998.  
  999.   /* Load random background image: */
  1000.  
  1001.   do
  1002.   {
  1003.     /* Don't pick the same one as last time... */
  1004.  
  1005.     i = rand() % NUM_BKGDS;
  1006.   }
  1007.   while (i == last_bkgd);
  1008.  
  1009.   last_bkgd = i;
  1010.  
  1011.   sprintf(fname, "%s/images/backgrounds/%d.jpg", DATA_PREFIX, i);
  1012.  
  1013.   if (bkgd != NULL)
  1014.     SDL_FreeSurface(bkgd);
  1015.  
  1016.   
  1017.   if (use_bkgd == 1)
  1018.   {
  1019.     bkgd = IMG_Load(fname);
  1020.     if (bkgd == NULL)
  1021.     {
  1022.       fprintf(stderr,
  1023.           "\nWarning: Could not load background image:\n"
  1024.           "%s\n"
  1025.           "The Simple DirectMedia error that ocurred was: %s\n",
  1026.           fname, SDL_GetError());
  1027.       use_bkgd = 0;
  1028.     }
  1029.   }
  1030.  
  1031.  
  1032.   /* Record score before this wave: */
  1033.  
  1034.   pre_wave_score = score;
  1035.  
  1036.  
  1037.   /* Set number of attackers for this wave: */
  1038.  
  1039.   num_attackers = 2 * wave;  /* FIXME: Is this good? */
  1040.  
  1041.  
  1042.   /* Set speed: */
  1043.  
  1044.   speed = wave + 1;
  1045.  
  1046.   if (speed > 10)
  1047.     speed = 10;
  1048. }
  1049.  
  1050.  
  1051. /* Add an comet to the game (if there's room): */
  1052.  
  1053. void add_comet(void)
  1054. {
  1055.   int i, found;
  1056.   
  1057.  
  1058.   /* Look for a free comet slot: */
  1059.   
  1060.   found = -1;
  1061.   
  1062.   for (i = 0; i < MAX_COMETS && found == -1; i++)
  1063.     {
  1064.       if (comets[i].alive == 0)
  1065.     {
  1066.       found = i;
  1067.     }
  1068.     }
  1069.   
  1070.   
  1071.   if (found != -1)
  1072.     {
  1073.       comets[found].alive = 1;
  1074.  
  1075.  
  1076.       /* Pick a city to attack: */
  1077.       
  1078.       i = rand() % NUM_CITIES;
  1079.      
  1080.  
  1081.       /* Set in to attack that city: */
  1082.       
  1083.       comets[found].city = i; 
  1084.  
  1085.  
  1086.       /* Start at the top, above the city in question: */
  1087.       
  1088.       comets[found].x = cities[i].x;
  1089.       comets[found].y = 0;
  1090.  
  1091.  
  1092.       /* Pick an operation (+, -, *, /): */
  1093.      
  1094.       do
  1095.       { 
  1096.         comets[found].oper = (rand() % NUM_OPERS);
  1097.       }
  1098.       while (opers[comets[found].oper] == 0);
  1099.      
  1100.       
  1101.       if (comets[found].oper == OPER_ADD)
  1102.     {
  1103.       /* Simple addition: */
  1104.         
  1105.       comets[found].eq1 = (rand() % 10);
  1106.       comets[found].eq2 = (rand() % 10);
  1107.       comets[found].answer = comets[found].eq1 + comets[found].eq2;
  1108.     }
  1109.       else if (comets[found].oper == OPER_SUB)
  1110.     {
  1111.       /* Subtraction: */
  1112.         
  1113.           comets[found].eq1 = (rand() % 10);
  1114.  
  1115.  
  1116.       /* (No negative answers) */
  1117.       /* [ WILL PROBABLY ALLOW FOR NEG. ANS. ] */
  1118.       
  1119.       do
  1120.       {
  1121.         comets[found].eq2 = (rand() % 10);
  1122.       }
  1123.       while (comets[found].eq2 > comets[found].eq1);
  1124.  
  1125.       comets[found].answer = comets[found].eq1 - comets[found].eq2;
  1126.     }
  1127.       else if (comets[found].oper == OPER_MULT)
  1128.     {
  1129.       /* Multiplication: */
  1130.     
  1131.           comets[found].eq1 = (rand() % 10);
  1132.       comets[found].eq2 = (rand() % 10);
  1133.       comets[found].answer = comets[found].eq1 * comets[found].eq2;
  1134.     }
  1135.       else if (comets[found].oper == OPER_DIV)
  1136.     {
  1137.       /* Division: */
  1138.     
  1139.       /* (Don't divide by zero) */
  1140.         
  1141.       comets[found].eq2 = (rand() % 9) + 1;
  1142.  
  1143.  
  1144.       /* (Make sure answer will be a whole (int) number) */
  1145.       
  1146.       comets[found].eq1 = (rand() % 10) * comets[found].eq2;
  1147.  
  1148.       
  1149.       comets[found].answer = comets[found].eq1 / comets[found].eq2;
  1150.     }
  1151.     }
  1152. }
  1153.  
  1154.  
  1155. /* Draw numbers/symbols over the attacker: */
  1156.  
  1157. void draw_nums(char * str, int x, int y)
  1158. {
  1159.   int i, j, cur_x, c;
  1160.   SDL_Rect src, dest;
  1161.  
  1162.  
  1163.   /* Center around the shape */
  1164.   
  1165.   cur_x = x - ((strlen(str) * (images[IMG_NUMS]->w / (10 + NUM_OPERS)))) / 2;
  1166.  
  1167.   if (cur_x < 0)
  1168.     cur_x = 0;
  1169.  
  1170.   if (cur_x + (strlen(str) * (images[IMG_NUMS]->w / (10 + NUM_OPERS))) >=
  1171.       screen->w)
  1172.     cur_x = ((screen->w) -
  1173.              (strlen(str) * (images[IMG_NUMS]->w / (10 + NUM_OPERS))));
  1174.  
  1175.  
  1176.   /* Draw each character: */
  1177.   
  1178.   for (i = 0; i < strlen(str); i++)
  1179.     {
  1180.       c = -1;
  1181.  
  1182.  
  1183.       /* Determine which character to display: */
  1184.       
  1185.       if (str[i] >= '0' && str[i] <= '9')
  1186.     c = str[i] - '0';
  1187.       else
  1188.     {
  1189.       /* [ THIS COULD CAUSE SLOWNESS... ] */
  1190.         
  1191.       for (j = 0; j < NUM_OPERS; j++)
  1192.         {
  1193.           if (str[i] == operchars[j])
  1194.         {
  1195.           c = 10 + j;
  1196.         }
  1197.         }
  1198.     }
  1199.       
  1200.  
  1201.       /* Display this character! */
  1202.       
  1203.       if (c != -1)
  1204.     {
  1205.       src.x = c * (images[IMG_NUMS]->w / (10 + NUM_OPERS));
  1206.       src.y = 0;
  1207.       src.w = (images[IMG_NUMS]->w / (10 + NUM_OPERS));
  1208.       src.h = images[IMG_NUMS]->h;
  1209.       
  1210.       dest.x = cur_x;
  1211.       dest.y = y - images[IMG_NUMS]->h;
  1212.       dest.w = src.w;
  1213.       dest.h = src.h;
  1214.       
  1215.       SDL_BlitSurface(images[IMG_NUMS], &src,
  1216.               screen, &dest);
  1217.  
  1218.  
  1219.           /* Move the 'cursor' one character width: */
  1220.  
  1221.       cur_x = cur_x + (images[IMG_NUMS]->w / (10 + NUM_OPERS));
  1222.     }
  1223.     }
  1224. }
  1225.  
  1226.  
  1227. /* Draw status numbers: */
  1228.  
  1229. void draw_numbers(char * str, int x)
  1230. {
  1231.   int i, cur_x, c;
  1232.   SDL_Rect src, dest;
  1233.  
  1234.  
  1235.   cur_x = x;
  1236.  
  1237.  
  1238.   /* Draw each character: */
  1239.   
  1240.   for (i = 0; i < strlen(str); i++)
  1241.     {
  1242.       c = -1;
  1243.  
  1244.  
  1245.       /* Determine which character to display: */
  1246.       
  1247.       if (str[i] >= '0' && str[i] <= '9')
  1248.     c = str[i] - '0';
  1249.       
  1250.  
  1251.       /* Display this character! */
  1252.       
  1253.       if (c != -1)
  1254.     {
  1255.       src.x = c * (images[IMG_NUMBERS]->w / 10);
  1256.       src.y = 0;
  1257.       src.w = (images[IMG_NUMBERS]->w / 10);
  1258.       src.h = images[IMG_NUMBERS]->h;
  1259.       
  1260.       dest.x = cur_x;
  1261.       dest.y = 0;
  1262.       dest.w = src.w;
  1263.       dest.h = src.h;
  1264.       
  1265.       SDL_BlitSurface(images[IMG_NUMBERS], &src,
  1266.               screen, &dest);
  1267.  
  1268.  
  1269.           /* Move the 'cursor' one character width: */
  1270.  
  1271.       cur_x = cur_x + (images[IMG_NUMBERS]->w / 10);
  1272.     }
  1273.     }
  1274. }
  1275.  
  1276.  
  1277. /* Pause loop: */
  1278.  
  1279. int pause_game(void)
  1280. {
  1281.   int done, quit;
  1282.   SDL_Event event;
  1283.   SDL_Rect dest;
  1284.  
  1285.   done = 0;
  1286.   quit = 0;
  1287.  
  1288.   dest.x = (screen->w - images[IMG_PAUSED]->w) / 2;
  1289.   dest.y = (screen->h - images[IMG_PAUSED]->h) / 2;
  1290.   dest.w = images[IMG_PAUSED]->w;
  1291.   dest.h = images[IMG_PAUSED]->h;
  1292.     
  1293.   SDL_BlitSurface(images[IMG_PAUSED], NULL, screen, &dest);
  1294.   SDL_Flip(screen);
  1295.  
  1296.  
  1297. #ifndef NOSOUND
  1298.   if (use_sound)
  1299.     Mix_PauseMusic();
  1300. #endif
  1301.   
  1302.   
  1303.   do
  1304.   {
  1305.     while (SDL_PollEvent(&event))
  1306.     {
  1307.       if (event.type == SDL_KEYDOWN)
  1308.     done = 1;
  1309.       else if (event.type == SDL_QUIT)
  1310.     quit = 1;
  1311.     }
  1312.  
  1313.     SDL_Delay(100);
  1314.   }
  1315.   while (!done && !quit);
  1316.  
  1317.  
  1318. #ifndef NOSOUND
  1319.   if (use_sound)
  1320.     Mix_ResumeMusic();
  1321. #endif
  1322.  
  1323.   return (quit);
  1324. }  
  1325.  
  1326.  
  1327.  
  1328. /* Draw a line: */
  1329.  
  1330. void draw_line(int x1, int y1, int x2, int y2, int red, int grn, int blu)
  1331. {
  1332.   int dx, dy, tmp;
  1333.   float m, b;
  1334.   Uint32 pixel;
  1335.   SDL_Rect dest;
  1336.  
  1337.   pixel = SDL_MapRGB(screen->format, red, grn, blu);
  1338.  
  1339.   dx = x2 - x1;
  1340.   dy = y2 - y1;
  1341.  
  1342.   putpixel(screen, x1, y1, pixel);
  1343.   
  1344.   if (dx != 0)
  1345.   {
  1346.     m = ((float) dy) / ((float) dx);
  1347.     b = y1 - m * x1;
  1348.  
  1349.     if (x2 > x1)
  1350.       dx = 1;
  1351.     else
  1352.       dx = -1;
  1353.  
  1354.     while (x1 != x2)
  1355.     {
  1356.       x1 = x1 + dx;
  1357.       y1 = m * x1 + b;
  1358.       
  1359.       putpixel(screen, x1, y1, pixel);
  1360.     }
  1361.   }
  1362.   else
  1363.   {
  1364.     if (y1 > y2)
  1365.     {
  1366.       tmp = y1;
  1367.       y1 = y2;
  1368.       y2 = tmp;
  1369.     }
  1370.     
  1371.     dest.x = x1;
  1372.     dest.y = y1;
  1373.     dest.w = 3;
  1374.     dest.h = y2 - y1;
  1375.  
  1376.     SDL_FillRect(screen, &dest, pixel);
  1377.   }
  1378. }
  1379.  
  1380.  
  1381. /* Draw a single pixel into the surface: */
  1382.  
  1383. void putpixel(SDL_Surface * surface, int x, int y, Uint32 pixel)
  1384. {
  1385. #ifdef PUTPIXEL_RAW
  1386.   int bpp;
  1387.   Uint8 * p;
  1388.   
  1389.   /* Determine bytes-per-pixel for the surface in question: */
  1390.   
  1391.   bpp = surface->format->BytesPerPixel;
  1392.   
  1393.   
  1394.   /* Set a pointer to the exact location in memory of the pixel
  1395.      in question: */
  1396.   
  1397.   p = (Uint8 *) (surface->pixels +       /* Start at beginning of RAM */
  1398.                  (y * surface->pitch) +  /* Go down Y lines */
  1399.                  (x * bpp));             /* Go in X pixels */
  1400.   
  1401.   
  1402.   /* Assuming the X/Y values are within the bounds of this surface... */
  1403.   
  1404.   if (x >= 0 && y >= 0 && x < surface -> w && y < surface -> h)
  1405.     {
  1406.       /* Set the (correctly-sized) piece of data in the surface's RAM
  1407.          to the pixel value sent in: */
  1408.       
  1409.       if (bpp == 1)
  1410.         *p = pixel;
  1411.       else if (bpp == 2)
  1412.         *(Uint16 *)p = pixel;
  1413.       else if (bpp == 3)
  1414.         {
  1415.           if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
  1416.             {
  1417.               p[0] = (pixel >> 16) & 0xff;
  1418.               p[1] = (pixel >> 8) & 0xff;
  1419.               p[2] = pixel & 0xff;
  1420.             }
  1421.           else
  1422.             {
  1423.               p[0] = pixel & 0xff;
  1424.               p[1] = (pixel >> 8) & 0xff;
  1425.               p[2] = (pixel >> 16) & 0xff;
  1426.             }
  1427.         }
  1428.       else if (bpp == 4)
  1429.         {
  1430.           *(Uint32 *)p = pixel;
  1431.         }
  1432.     }
  1433. #else
  1434.   SDL_Rect dest;
  1435.  
  1436.   dest.x = x;
  1437.   dest.y = y;
  1438.   dest.w = 3;
  1439.   dest.h = 4;
  1440.  
  1441.   SDL_FillRect(surface, &dest, pixel);
  1442. #endif
  1443. }
  1444.  
  1445.  
  1446. /* Draw image at lower center of screen: */
  1447.  
  1448. void draw_console_image(int i)
  1449. {
  1450.   SDL_Rect dest;
  1451.  
  1452.   dest.x = (screen->w - images[i]->w) / 2;
  1453.   dest.y = (screen->h - images[i]->h);
  1454.   dest.w = images[i]->w;
  1455.   dest.h = images[i]->h;
  1456.  
  1457.   SDL_BlitSurface(images[i], NULL, screen, &dest);
  1458. }
  1459.  
  1460.  
  1461. /* Increment score: */
  1462.  
  1463. void add_score(int inc)
  1464. {
  1465.   score += inc;
  1466. }
  1467.  
  1468.  
  1469.